package org.asiainfo.flowable.service.impl;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.mybatisflex.core.paginate.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.asiainfo.common.core.constant.ProcessConstants;
import org.asiainfo.common.core.enums.FormType;
import org.asiainfo.common.core.exception.ServiceException;
import org.asiainfo.common.core.utils.SpringUtils;
import org.asiainfo.common.core.utils.StringUtils;
import org.asiainfo.common.flowable.factory.FlowServiceFactory;
import org.asiainfo.common.flowable.utils.ModelUtils;
import org.asiainfo.common.json.utils.JsonUtils;
import org.asiainfo.common.mybatis.stream.core.page.PageQuery;
import org.asiainfo.common.mybatis.stream.core.page.TableDataInfo;
import org.asiainfo.common.satoken.utils.LoginHelper;
import org.asiainfo.flowable.domain.bo.WfModelBo;
import org.asiainfo.flowable.domain.vo.WfFormVo;
import org.asiainfo.flowable.domain.vo.WfModelVo;
import org.asiainfo.flowable.model.WfMetaInfoDTO;
import org.asiainfo.flowable.service.WfDeployFormService;
import org.asiainfo.flowable.service.WfFormService;
import org.asiainfo.flowable.service.WfModelService;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.StartEvent;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ModelQuery;
import org.flowable.engine.repository.ProcessDefinition;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;

/**
 * 流程模型管理服务实现类
 *
 * @author dotor-ww
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class WfModelServiceImpl extends FlowServiceFactory implements WfModelService {

    /**
     * 分页查询流程模型
     *
     * @param wfModelBo 流程模型对象
     * @param pageQuery 分页
     * @return 流程模型
     */
    @Override
    public TableDataInfo<WfModelVo> queryPageList(WfModelBo wfModelBo, PageQuery pageQuery) {
        ModelQuery modelQuery = repositoryService
            .createModelQuery()
            .latestVersion()
            .orderByCreateTime()
            .desc();

        if (StringUtils.isNotBlank(wfModelBo.getModelKey())) { // 模型标识
            modelQuery.modelKey(wfModelBo.getModelKey());
        }
        if (StringUtils.isNotBlank(wfModelBo.getModelName())) { // 模型名称
            modelQuery.modelName("%" + wfModelBo.getModelName() + "%");
        }
        if (StringUtils.isNotBlank(wfModelBo.getCategory())) { // 流程分类
            modelQuery.modelCategory(wfModelBo.getCategory());
        }
        long pageTotal = modelQuery.count();
        if (pageTotal <= 0) {
            return TableDataInfo.build();
        }
        int offSet = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
        List<Model> models = modelQuery.listPage(offSet, pageQuery.getPageSize());
        List<WfModelVo> modelVos = models.stream()
            .map(model -> {
                WfModelVo wfModelVo = new WfModelVo();
                wfModelVo.setModelId(model.getId());
                wfModelVo.setModelName(model.getName());
                wfModelVo.setModelKey(model.getKey());
                wfModelVo.setCategory(model.getCategory());
                wfModelVo.setCreateTime(model.getCreateTime());
                wfModelVo.setVersion(model.getVersion());
                WfMetaInfoDTO wfMetaInfoDTO = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDTO.class);
                if (ObjectUtil.isNotNull(wfMetaInfoDTO)) {
                    wfModelVo.setDescription(wfMetaInfoDTO.getDescription());
                    wfModelVo.setFormType(wfMetaInfoDTO.getFormType());
                    wfModelVo.setFormId(wfMetaInfoDTO.getFormId());
                }
                return wfModelVo;
            }).toList();
        Page<WfModelVo> page = new Page<>(modelVos, pageQuery.getPageNum(), pageQuery.getPageSize(), pageTotal);
        return TableDataInfo.build(page);
    }

    /**
     * 分页查询流程模型历史
     *
     * @param wfModelBo 流程模型对象
     * @param pageQuery 分页
     * @return 历史流程模型
     */
    @Override
    public TableDataInfo<WfModelVo> historyList(WfModelBo wfModelBo, PageQuery pageQuery) {
        ModelQuery modelQuery = repositoryService
            .createModelQuery()
            .latestVersion()
            .orderByCreateTime()
            .desc();
        // 1.去掉最新版本
        long pageTotal = modelQuery.count() - 1;
        if (pageTotal <= 0) {
            return TableDataInfo.build();
        }
        // 2.添加最新版本
        int offSet = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1) + 1;
        // 3.分页查询
        List<Model> models = modelQuery.listPage(offSet, pageQuery.getPageSize());
        List<WfModelVo> modelVos = models.stream()
            .map(model -> {
                WfModelVo wfModelVo = new WfModelVo();
                wfModelVo.setModelId(model.getId());
                wfModelVo.setModelName(model.getName());
                wfModelVo.setModelKey(model.getKey());
                wfModelVo.setCategory(model.getCategory());
                wfModelVo.setCreateTime(model.getCreateTime());
                wfModelVo.setVersion(model.getVersion());
                WfMetaInfoDTO wfMetaInfoDTO = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDTO.class);
                if (ObjectUtil.isNotNull(wfMetaInfoDTO)) {
                    wfModelVo.setDescription(wfMetaInfoDTO.getDescription());
                    wfModelVo.setFormType(wfMetaInfoDTO.getFormType());
                    wfModelVo.setFormId(wfMetaInfoDTO.getFormId());
                }
                return wfModelVo;
            }).toList();
        Page<WfModelVo> page = new Page<>(modelVos, pageQuery.getPageNum(), pageQuery.getPageSize(), pageTotal);
        return TableDataInfo.build(page);
    }

    /**
     * 查询流程模型详细信息
     *
     * @param modelId 流程模型ID
     * @return 流程模型
     */
    @Override
    public WfModelVo queryById(String modelId) {
        Model model = repositoryService.getModel(modelId);
        if (ObjectUtil.isNull(model)) {
            throw new ServiceException("流程模型不存在");
        }
        String bpmnXml = queryBpmnXmlById(modelId);
        WfModelVo wfModelVo = new WfModelVo();
        wfModelVo.setModelId(model.getId());
        wfModelVo.setModelName(model.getName());
        wfModelVo.setBpmnXml(bpmnXml);
        wfModelVo.setCategory(model.getCategory());
        wfModelVo.setModelKey(model.getKey());
        wfModelVo.setVersion(model.getVersion());
        wfModelVo.setCreateTime(model.getCreateTime());
        WfMetaInfoDTO wfMetaInfoDTO = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDTO.class);
        if (ObjectUtil.isNotNull(wfMetaInfoDTO)) {
            wfModelVo.setDescription(wfMetaInfoDTO.getDescription());
            wfModelVo.setFormType(wfMetaInfoDTO.getFormType());
            wfModelVo.setFormId(wfMetaInfoDTO.getFormId());
            if (FormType.PROCESS.getType().equals(wfMetaInfoDTO.getFormType())) {
                WfFormVo wfFormVo = SpringUtils.getBean(WfFormService.class).queryById(wfMetaInfoDTO.getFormId());
                wfModelVo.setRule(wfFormVo.getRule());
                wfModelVo.setOption(wfModelVo.getOption());
            }
        }
        return null;
    }

    /**
     * 获取流程模型表单数据
     *
     * @param modelId 流程模型ID
     * @return 流程模型表单数据
     */
    @Override
    public String queryBpmnXmlById(String modelId) {
        byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
        return StrUtil.utf8Str(bpmnBytes);
    }

    /**
     * 新增流程模型
     *
     * @param wfModelBo 流程模型对象
     */
    @Override
    public void insertByBo(WfModelBo wfModelBo) {
        Model model = repositoryService.newModel();
        model.setName(wfModelBo.getModelName());
        model.setKey(wfModelBo.getModelKey());
        model.setCategory(wfModelBo.getCategory());
        String metaInfo = buildMetaInfo(new WfMetaInfoDTO(), wfModelBo.getDescription());
        model.setMetaInfo(metaInfo);
        model.setTenantId(String.valueOf(LoginHelper.getTenantId()));
        repositoryService.saveModel(model);
    }

    /**
     * 修改流程模型
     *
     * @param wfModelBo 流程模型对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateByBo(WfModelBo wfModelBo) {
        Model model = repositoryService.getModel(wfModelBo.getModelId());
        if(ObjectUtil.isNull(model)){
            throw new ServiceException("流程模型不存在");
        }
        model.setCategory(wfModelBo.getCategory());
        WfMetaInfoDTO wfMetaInfoDTO = JsonUtils.parseObject(model.getMetaInfo(), WfMetaInfoDTO.class);
        String metaInfo = buildMetaInfo(wfMetaInfoDTO, wfModelBo.getDescription());
        model.setMetaInfo(metaInfo);
        repositoryService.saveModel(model);
    }

    /**
     * 保存流程模型
     *
     * @param wfModelBo 流程模型对象
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(WfModelBo wfModelBo) {
        Model model = repositoryService.getModel(wfModelBo.getModelId());
        if(ObjectUtil.isNull(model)){
            throw new ServiceException("流程模型不存在");
        }
        BpmnModel bpmnModel = ModelUtils.getBpmnModel(wfModelBo.getBpmnXml());
        if(ObjectUtil.isEmpty(bpmnModel)){
            throw new ServiceException("获取模型定义失败");
        }
        String processName = bpmnModel.getMainProcess().getName();
        StartEvent startEvent = ModelUtils.getStartEvent(bpmnModel);
        if(ObjectUtil.isNull(startEvent)){
            throw new ServiceException("开始节点不存在，请检查流程设计是否有误");
        }
        if(StringUtils.isNotBlank(startEvent.getFormKey())){
            throw new ServiceException("请先配置流程表单");
        }
        Model newModel = model;
        newModel.setName(processName);
        if(wfModelBo.getNewVersion()){
            newModel = repositoryService.newModel();
            newModel.setName(processName);
            newModel.setKey(model.getKey());
            newModel.setMetaInfo(model.getMetaInfo());
            newModel.setCategory(model.getCategory());
            newModel.setVersion(model.getVersion() + 1);
        }
        repositoryService.saveModel(newModel);
        byte[] bpmnXmlBytes = StringUtils.getBytes(wfModelBo.getBpmnXml(), StandardCharsets.UTF_8);
        repositoryService.addModelEditorSource(newModel.getId(), bpmnXmlBytes);
    }

    /**
     * 删除流程模型
     *
     * @param modelIds 流程模型ID集合
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteWithValidByIds(Collection<String> modelIds) {
        for (String modelId : modelIds) {
            Model model = repositoryService.getModel(modelId);
            if(ObjectUtil.isNull(model)){
                throw new ServiceException("流程模型不存在");
            }
            repositoryService.deleteModel(modelId);
        }
    }

    /**
     * 设置流程模型为最新
     *
     * @param modelId 流程模型ID
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void latestModel(String modelId) {
        Model model = repositoryService.getModel(modelId);
        if(ObjectUtil.isNull(model)){
            throw new ServiceException("流程模型不存在");
        }
        Integer latestVersion = repositoryService
            .createModelQuery()
            .modelKey(model.getKey())
            .latestVersion()
            .singleResult()
            .getVersion();

        if (model.getVersion().equals(latestVersion)) {
            throw new ServiceException("当前版本已是最新版本");
        }
        byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
        Model newModel = repositoryService.newModel();
        newModel.setName(model.getName());
        newModel.setKey(model.getKey());
        newModel.setCategory(model.getCategory());
        newModel.setMetaInfo(model.getMetaInfo());
        newModel.setVersion(latestVersion + 1);
        repositoryService.saveModel(newModel);
        repositoryService.addModelEditorSource(newModel.getId(), bpmnBytes);
    }

    /**
     * 部署流程模型
     *
     * @param modelId 流程模型ID
     * @return 部署结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean deployModel(String modelId) {
        Model model = repositoryService.getModel(modelId);
        if(ObjectUtil.isNull(model)){
            throw new ServiceException("流程模型不存在");
        }
        byte[] bpmnBytes = repositoryService.getModelEditorSource(modelId);
        if(ArrayUtil.isEmpty(bpmnBytes)){
            throw new ServiceException("请先设计流程图");
        }
        String bpmnXmlStr = StringUtils.toEncodedString(bpmnBytes, StandardCharsets.UTF_8);
        BpmnModel bpmnModel = ModelUtils.getBpmnModel(bpmnXmlStr);
        String processName = model.getName()+ ProcessConstants.SUFFIX;
        Deployment deployment = repositoryService
            .createDeployment()
            .name(model.getName())
            .key(model.getKey())
            .category(model.getCategory())
            .addBytes(processName, bpmnBytes)
            .deploy();

        ProcessDefinition processDefinition = repositoryService
            .createProcessDefinitionQuery()
            .deploymentId(deployment.getId())
            .singleResult();

        repositoryService.setProcessDefinitionCategory(processDefinition.getId(), model.getCategory());
        return SpringUtils.getBean(WfDeployFormService.class).saveInternalDeployForm(deployment.getId(), bpmnModel);
    }

    /**
     * 构建模型扩展信息
     *
     * @param wfMetaInfoDTO 流程扩展信息对象
     * @return 模型扩展信息
     */
    private String buildMetaInfo(WfMetaInfoDTO wfMetaInfoDTO, String desc) {
        if (StringUtils.isNotBlank(desc)) {
            wfMetaInfoDTO.setDescription(desc);
        }
        if (ObjectUtil.isNull(wfMetaInfoDTO.getCreateUser())) {
            wfMetaInfoDTO.setCreateUser(LoginHelper.getUsername());
        }
        return JsonUtils.toJsonString(wfMetaInfoDTO);
    }
}
